﻿using System;
using System.Collections.Generic;
using System.Linq;
using CI.DAL;
using CI.Mesopotamia.Dal;
using CI.Mesopotamia.WebService;
using CI.Misc;
using CI.Organism;
using NUnit.Framework;
using CI.Interfaces;
using CI.Mesopotamia;
using CI;

namespace MesopotamiaDCProxy.SystemTests
{
    [TestFixture]
    public class DalTests
    {
        InboundClient _wsClient;
        const string ScenarioName = "Survival of the Fattest - System Tests - DAL";
        const string HostVersion = "hostVersion";
        const string MachineName = "machineName";
        MSRBHost _hostingEnvironment;
        DALList _organisms;
        Scenario _scenario;
        int _userId = 0;
        const int OrganismCount = 0;
        private List<Guid> _organismsToLoad;

        /// <summary>
        /// setup tests
        /// </summary>
        [SetUp]
        public void Setup()
        {

            var endpointName = "Localhost";
            _organismsToLoad = new List<Guid>();
#if PUBLISHDEBUG
            endpointName = "Grid";
#endif
            _wsClient = new InboundClient(endpointName);

            _hostingEnvironment = new MSRBHost(new MockHostingEnvironmentPhysical(), new CI.Mesopotamia.ScenarioLocalXML());
            _organisms = new DALList();

            // load dal
            IDAL dal = new TestDAL();
            _hostingEnvironment = (MSRBHost)dal.LoadHostingEnvironment("dont care", _hostingEnvironment, 0, _organismsToLoad);

            var counter = 100;

            // todo : find out why i cant cast a list of types to another type
            foreach (IOrganism organism in _hostingEnvironment.Organisms.Alive)
            {
                _organisms.Add((Organism)organism);
                organism.FitnessValue = counter;
            }

            CreateScenario();


        }

        private void CreateScenario()
        {
            _scenario = new CI.Mesopotamia.ScenarioLocalXML();
            _scenario.FitnessFunction = typeof(CI.Mesopotamia.CollisionDetection).ToString();
            _scenario.ScenarioName = ScenarioName;
            _scenario.ScenarioDescription = "The evolutionary objective is for the fattest one to reproduce and evolve";
            _scenario.ManifestFile = "InABox.Manifest.xml";
            _scenario.BoundryXRadiusMax = 3.2f;
            _scenario.BoundryYRadiusMax = 2.5f;
            _scenario.BoundryZRadiusMax = 3.2f;
            _scenario.SimulationRestrictions = typeof(CI.Mesopotamia.SurvivalFattest).ToString();
            _scenario.RequiredOrganisms = new List<IScenarioEntityRequirements>();
            _hostingEnvironment = new CI.Mesopotamia.MSRBHost(new MockHostingEnvironmentPhysical(), _scenario);
            Scenario.ScenarioEntityRequirements _amoebas = new Scenario.ScenarioEntityRequirements();
            Scenario.ScenarioEntityRequirements _iRobots = new Scenario.ScenarioEntityRequirements();
            _scenario.RequiredOrganisms.Add(_amoebas);
            _scenario.RequiredOrganisms.Add(_iRobots);
            _amoebas.OrganismType = new Amoeba(new Organism(_hostingEnvironment), Guid.NewGuid());
            _iRobots.OrganismType = new iRobot(new Organism(_hostingEnvironment), Guid.NewGuid());

            // null out sub sections
            _amoebas.OrganismType.Organism = null;
            _iRobots.OrganismType.Organism = null;
            _amoebas.OrganismType.Guid = Guid.Empty;
            _iRobots.OrganismType.Guid = Guid.Empty;
            _amoebas.UseFallBackOnly = true;
            _iRobots.UseFallBackOnly = false;

            // add the location
            _amoebas.Locations.Add(new Location(-1.5f, 0.1f, 1.5f, 0.1f, 0.1f, 0.1f));
            _amoebas.Locations.Add(new Location(-1.5f, 0.1f, -1.5f, 0.1f, 0.1f, 0.1f));
            _amoebas.Locations.Add(new Location(1.5f, 0.1f, 1.5f, 0.1f, 0.1f, 0.1f));
            _amoebas.Locations.Add(new Location(1.5f, 0.1f, -1.5f, 0.1f, 0.1f, 0.1f));
            _iRobots.Locations.Add(new Location(0.5f, 0.1f, 0, 0.2f, 0.1f, 0.2f));
            _iRobots.Locations.Add(new Location(-0.5f, 0.1f, 0, 0.2f, 0.1f, 0.2f));

            // add the fall back organisms
            _scenario.FallBackOrganisms = new List<IOrganism>(_hostingEnvironment.Organisms.Alive);
        }

        /// <summary>
        /// tear down
        /// </summary>
        [TearDown]
        public void TearDown()
        {
            // Cleanup
#if !PUBLISHRELEASE
            _wsClient.DeleteScenario(_scenario.ScenarioName, _userId);
#endif

            _wsClient.Close();
        }

#if DEBUG
        /// <summary>
        /// save a dal via the web service to the DB then load them and compare them, duplicate organisms count
        /// </summary>
        [Test]
        public void SaveLoadDALGridWSDuplicateOrganisms()
        {
            // get user ID
            _userId = _wsClient.GetNewUserId(MachineName, HostVersion);

            // delete the old scenario if exists
            _wsClient.DeleteScenario(_scenario.ScenarioName, _userId);

            // save the new scenario
            _wsClient.SaveScenario(_scenario, _userId);

            // add an the first organism to the list again
            _organisms.Add(_organisms[0]);

            // save the DAL
            var saveCount = _wsClient.SaveDal(_scenario.ScenarioName, _organisms, _userId);

            // check and make sure the save count is the same
            Assert.AreEqual(_organisms.Count - 1, saveCount, "Saved count in dal is different to orginal count");

        }

        /// <summary>
        /// save a dal via the web service to the DB then load them and compare them
        /// </summary>
        [Test]
        public void SaveLoadDALGridWSCompare()
        {
            // get user ID
            _userId = _wsClient.GetNewUserId(MachineName, HostVersion);

            // delete the old scenario if exists
            _wsClient.DeleteScenario(_scenario.ScenarioName, _userId);

            // adjust first org initCIET
            _organisms[0].InitCIET = 12345;

            // save the new scenario
            _wsClient.SaveScenario(_scenario, _userId);

            // save the DAL
            var saveCount = _wsClient.SaveDal(_scenario.ScenarioName, _organisms, _userId);

            // check and make sure the save count is the same
            Assert.AreEqual(saveCount, _organisms.Count, "Saved count in dal is different to orginal count");

            // load it back in
            var loadedOrganisms = _wsClient.LoadDal(_scenario.ScenarioName, _userId, OrganismCount, _organismsToLoad.ToArray());

            // compare it
            Assert.IsTrue(CompareOrgList(_organisms, loadedOrganisms), "Saved and loaded orgs not the same");
        }

        /// <summary>
        /// Load a DAL from xml, save it to the grid, load it back then compare against origanal(webservice)
        /// </summary>
        [Test]
        public void LoadXMLSaveGridLoadGridCompareDALWS()
        {
            // get user ID
            _userId = _wsClient.GetNewUserId(MachineName, HostVersion);

            // delete the old scenario if exists
            _wsClient.DeleteScenario(_scenario.ScenarioName, _userId);

            // save the new scenario
            _wsClient.SaveScenario(_scenario, _userId);

            // load the test dal
            var xmlDal = new XMLDAL();
            IHostingEnvironment testhosting = new MSRBHost(new MockHostingEnvironmentPhysical(), new ScenarioLocalXML());

            testhosting = xmlDal.LoadHostingEnvironment("test", testhosting, 0, _organismsToLoad.ToList());

            Assert.IsNotNull(testhosting, "Unable to load hosting environment");

            // grid dal
            testhosting.FitnessFunction = new CollisionDetection();
            var dalList = GridWSDAL.CreateUploadList(testhosting);

            // save the DAL
            _wsClient.SaveDal(_scenario.ScenarioName, dalList, _userId);

            // load it back in
            List<Organism> loadedOrganisms = _wsClient.LoadDal(_scenario.ScenarioName, _userId, OrganismCount, _organismsToLoad.ToArray());

            // compare it
            Assert.IsTrue(CompareOrgList(loadedOrganisms, dalList), "Saved and loaded orgs not the same");
        }

#endif
        /// <summary>
        /// Compare two lists to make sure they are the same
        /// </summary>
        /// <param name="list1"></param>
        /// <param name="list2"></param>
        /// <returns></returns>
        private static bool CompareOrgList(List<Organism> list1, List<Organism> list2)
        {
            // make sure they are both defined
            if (list1 == null)
                return false;
            if (list2 == null)
                return false;
            // check the counts
            if (list1.Count != list2.Count)
                return false;

            // go through the list
            foreach (IOrganism organism in list1)
            {

                #region Check to make sure its not already in the list
                // if it already in the list dont add it
                IOrganism foundOrganism = null;
                try
                {
                    foundOrganism = list2.First(organismFind => organismFind.Guid == organism.Guid);
                }
                catch (InvalidOperationException)
                {
#if DEBUG
                    return false;
#endif
                }
                #endregion

                if (!organism.EqualsValueTypes(foundOrganism))
                    return false;
            }
            return true;
        }

    }
}
